Multi-category dot-density maps often work well when the categories cluster geographically. Recent immigrants by country of origin work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.
#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors=c("v_CA11N_265","v_CA11N_268","v_CA11N_304","v_CA11N_334","v_CA11N_373","v_CA11N_376","v_CA11N_379","v_CA11N_382")
We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.
categories=c("Americas","Europe","Africa","Philippines","China","India","Other Asian Countries")
colors=c("#7a0177", "#3333cc", "#ff00ff", "#00ffff", "#ff1a1c", "#4dff4a", "#ffff33")
prep_data <- function(geo){
data <- geo@data %>% replace(is.na(.), 0)
data <- rename(data,
total=v_CA11N_265,
Americas=v_CA11N_268,
Europe=v_CA11N_304,
Africa=v_CA11N_334,
Philippines=v_CA11N_376,
China=v_CA11N_379,
India=v_CA11N_382)
data <- mutate(data,`Other Asian Countries` = v_CA11N_373-Philippines-China-India)
geo@data <- data
return(geo)
}
Next we grab the data via cancensus,
data_csd=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CSD') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_5944ecb0017405460024293a4e1245b4.geojson", layer: "OGRGeoJSON"
with 39 features
It has 12 fields
data_ct=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CT') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_2c2ba582c4fbc552cd7375e1cdb94585.geojson", layer: "OGRGeoJSON"
with 457 features
It has 12 fields
data_da=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='DA') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_6b5f32d4a2ad20b88dbcddc4dc419562.geojson", layer: "OGRGeoJSON"
with 3438 features
It has 12 fields
data_db=get_census(dataset = 'CA11', regions=regions,geo_format='sp',labels='short',level='DB')
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_1cc7cbd8f3815230304992d45a56ef3b.geojson", layer: "OGRGeoJSON"
with 15479 features
It has 10 fields
which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.
data_ct@data <- dot_density.proportional_re_aggregate(data=data_ct@data,parent_data=data_csd@data,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da@data <- dot_density.proportional_re_aggregate(data=data_da@data,parent_data=data_ct@data,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db@data <- dot_density.proportional_re_aggregate(data=data_db@data,parent_data=data_da@data,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")
Map
All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap.
# 1 dot = 5 immigrants
scale=5
#xlim = c(-123.34, -122.51), ylim = c(49, 49.4)
dots.db <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale)
basemap +
coord_fixed(xlim=c(-123.29,-122.6), ylim=c(49.02,49.35), ratio = 1/cos(49.2/180*pi)) +
# shade unpopulated blocks
# geom_polygon(data=data_db[data_db$Population<=5,],
# aes(long, lat, group = group),
# fill = "#222222", size=0.1,
# color = "#222222") +
scale_colour_manual(values = colors) +
labs(color = "",
title="Immigrants 2006 - 2011",
caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
subtitle = paste0("1 dot = ",scale," people")) +
dot_density.dots_map(dots=dots.db,alpha=0.75,size=0.25)

# save image for later
ggsave('../images/recent_immigrants.png',width=26,height=26)
LS0tCnRpdGxlOiAiVmlnbmV0dGUgVGl0bGUiCmF1dGhvcjogIlZpZ25ldHRlIEF1dGhvciIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXtWaWduZXR0ZSBUaXRsZX0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KCk11bHRpLWNhdGVnb3J5IGRvdC1kZW5zaXR5IG1hcHMgb2Z0ZW4gd29yayB3ZWxsIHdoZW4gdGhlIGNhdGVnb3JpZXMgY2x1c3RlciBnZW9ncmFwaGljYWxseS4gUmVjZW50IGltbWlncmFudHMgYnkKY291bnRyeSBvZiBvcmlnaW4gd29yayB3ZWxsIGZvciB0aGlzLgojIyBEYXRhCkZpcnN0IHdlIGdyYWIgdGhlIGltbWlncmFudCBkYXRhIHZpYSBbY2FuY2Vuc3VzXShodHRwczovL2dpdGh1Yi5jb20vbW91bnRhaW5NYXRoL2NhbmNlbnN1cyksIG1ha2luZyB1c2Ugb2YgdGhlIFtDZW5zdXNNYXBwZXIgQVBJIHRvb2xdKGh0dHBzOi8vY2Vuc3VzbWFwcGVyLmNhL2FwaS9DQTExKSB0byBzZWxlY3QgdGhlIHJlZ2lvbnMgYW5kIHZhcmlhYmxlcyB3ZSBuZWVkLgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibW91bnRhaW5tYXRoL2NhbmNlbnN1cyIpCmxpYnJhcnkoY2FuY2Vuc3VzKQpsaWJyYXJ5KGRvdGRlbnNpdHkpCiMgb3B0aW9ucyhjYW5jZW5zdXMuYXBpX2tleT0neW91cl9hcGlfa2V5JykKcmVnaW9ucz1saXN0KENNQT0iNTk5MzMiKQp2ZWN0b3JzPWMoInZfQ0ExMU5fMjY1Iiwidl9DQTExTl8yNjgiLCJ2X0NBMTFOXzMwNCIsInZfQ0ExMU5fMzM0Iiwidl9DQTExTl8zNzMiLCJ2X0NBMTFOXzM3NiIsInZfQ0ExMU5fMzc5Iiwidl9DQTExTl8zODIiKQpgYGAKCldlIGNob29zZSB0aGUgY2F0ZWdvcmllcyBhbmQgY29sb3VycyB3ZSB3YW50IHRvIG1hcCBhbmQgZGVmaW5lIGEgY29udmVuaWVuY2UgZnVuY3Rpb24gdG8gcmVuYW1lIHRoZSB2YXJpYWJsZXMgYW5kIGNvbXB1dGUgdGhlIHFhbnRpdGllcyBmb3IgdGhlIG90aGVyIGFzaWFuIGNvdW50cmllcyB0aGF0IHdlIGRvbid0IGJyZWFrIG91dC4KYGBge3J9CmNhdGVnb3JpZXM9YygiQW1lcmljYXMiLCJFdXJvcGUiLCJBZnJpY2EiLCJQaGlsaXBwaW5lcyIsIkNoaW5hIiwiSW5kaWEiLCJPdGhlciBBc2lhbiBDb3VudHJpZXMiKQpjb2xvcnM9YygiIzdhMDE3NyIsICIjMzMzM2NjIiwgIiNmZjAwZmYiLCAiIzAwZmZmZiIsICIjZmYxYTFjIiwgIiM0ZGZmNGEiLCAiI2ZmZmYzMyIpCgpwcmVwX2RhdGEgPC0gZnVuY3Rpb24oZ2VvKXsKICBkYXRhIDwtIGdlb0BkYXRhICU+JSByZXBsYWNlKGlzLm5hKC4pLCAwKQogIGRhdGEgPC0gcmVuYW1lKGRhdGEsCiAgICB0b3RhbD12X0NBMTFOXzI2NSwKICAgIEFtZXJpY2FzPXZfQ0ExMU5fMjY4LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIEV1cm9wZT12X0NBMTFOXzMwNCwKICAgIEFmcmljYT12X0NBMTFOXzMzNCwKICAgIFBoaWxpcHBpbmVzPXZfQ0ExMU5fMzc2LAogICAgQ2hpbmE9dl9DQTExTl8zNzksCiAgICBJbmRpYT12X0NBMTFOXzM4MikKICBkYXRhIDwtIG11dGF0ZShkYXRhLGBPdGhlciBBc2lhbiBDb3VudHJpZXNgID0gdl9DQTExTl8zNzMtUGhpbGlwcGluZXMtQ2hpbmEtSW5kaWEpCiAgZ2VvQGRhdGEgPC0gZGF0YQogIHJldHVybihnZW8pCn0KYGBgCgoKTmV4dCB3ZSBncmFiIHRoZSBkYXRhIHZpYSBgY2FuY2Vuc3VzYCwKYGBge3IsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YV9jc2Q9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTEnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nQ1NEJykgJT4lIHByZXBfZGF0YQpkYXRhX2N0PWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0NUJykgJT4lIHByZXBfZGF0YQpkYXRhX2RhPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0RBJykgJT4lIHByZXBfZGF0YQpkYXRhX2RiPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLGdlb19mb3JtYXQ9J3NwJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nREInKQpgYGAKCndoaWNoIHdlIHRoZW4gcmUtYWdncmVnYXRlIHRvIG1ha2Ugc3VyZSB3ZSBkb24ndCBtaXNzIG92ZXJhbGwgY291bnRzIGR1ZSB0byBwcml2YWN5IGN1dG9mZnMgZGlzdHJpYnV0ZSB0aGVtCnByb3BvcnRpb25hbGx5IGFtb25nIHRoZSBwb3B1bGF0aW9uLgpgYGB7cn0KZGF0YV9jdEBkYXRhIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2N0QGRhdGEscGFyZW50X2RhdGE9ZGF0YV9jc2RAZGF0YSxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNTRF9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmRhdGFfZGFAZGF0YSA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9kYUBkYXRhLHBhcmVudF9kYXRhPWRhdGFfY3RAZGF0YSxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNUX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKZGF0YV9kYkBkYXRhIDwtIGRvdF9kZW5zaXR5LnByb3BvcnRpb25hbF9yZV9hZ2dyZWdhdGUoZGF0YT1kYXRhX2RiQGRhdGEscGFyZW50X2RhdGE9ZGF0YV9kYUBkYXRhLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiREFfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpiZ19jb2xvcj0iIzExMTExMSIKYmFzZV9jb2xvcj0iIzMzMzMzMyIKdGV4dF9jb2xvcj0iI2VlZWVlZSIKdGhlbWVfb3B0czwtbGlzdCh0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gYmdfY29sb3IsIGNvbG91ciA9IE5BKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1iZ19jb2xvciwgc2l6ZT0xLGxpbmV0eXBlPSJzb2xpZCIsY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9ODAsaGp1c3QgPSAwLjUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT02MCxoanVzdCA9IDAuNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemU9MjUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTQwLCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTQwLCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWJnX2NvbG9yLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIixjb2xvcj1iZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gYmdfY29sb3IsY29sb3IgPSBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMywgJ2xpbmVzJyksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKQoKYmFzZW1hcCA8LSAgIGdncGxvdChkYXRhX2NzZCkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLCBmaWxsID0gYmFzZV9jb2xvciwgc2l6ZT0wLjEsIGNvbG9yID0gJ2dyZXknKSArCiAgICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG5yb3c9MSxvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MTUpKSkgKwogICAgY29vcmRfbWFwKHByb2plY3Rpb249ImxhbWJlcnQiLCBsYXQwPTQ5LCBsYXQ9NDkuNCkgKwogICAgdGhlbWVfb3B0cwoKYGBgCgoKIyNNYXAKQWxsIHRoYXQncyBsZWZ0IHRvIGRvIGlzIHRvIGNvdmVydCBvdXIgcmUtYWdncmVnYXRlZCBibG9jay1sZXZlbCBkYXRhIHRvIGRvdHMsIHVzaW5nIHRoZSBgZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzYApmdW5jdGlvbiBmcm9tIHRoZSBbYGRvdGRlbnNpdHlgIHBhY2thZ2VdKCkgYW5kIGZlZWQgaXQgaW50byB0aGUgYGRvdF9kZW5zaXR5LmRvdHNfbWFwYCBmdW5jdGlvbiB0byBhZGQgdGhlbSB0bwpvdXIgYmFzZW1hcC4KYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMywgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUV9CiMgMSBkb3QgPSA1IGltbWlncmFudHMKc2NhbGU9NQoKCmRvdHMuZGIgPC0gZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzKGdlb19kYXRhID0gZGF0YV9kYiwgY2F0ZWdvcmllcyA9IGNhdGVnb3JpZXMsIHNjYWxlPXNjYWxlKQpiYXNlbWFwICsKICAjIHpvb20gaW4gYSBiaXQKICBjb29yZF9maXhlZCh4bGltPWMoLTEyMy4yOSwtMTIyLjYpLCB5bGltPWMoNDkuMDIsNDkuMzUpLCByYXRpbyA9IDEvY29zKDQ5LjIvMTgwKnBpKSkgKwojIHNoYWRlIHVucG9wdWxhdGVkIGJsb2NrcwojICBnZW9tX3BvbHlnb24oZGF0YT1kYXRhX2RiW2RhdGFfZGIkUG9wdWxhdGlvbjw9NSxdLCAKIyAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApLCAKIyAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiIzIyMjIyMiIsIHNpemU9MC4xLCAKIyAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMyMjIyMjIiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpICsKICBsYWJzKGNvbG9yID0gIiIsCiAgICAgICAgICAgICAgICB0aXRsZT0iSW1taWdyYW50cyAyMDA2IC0gMjAxMSIsCiAgICAgICAgICAgICAgICBjYXB0aW9uPSJTb3VyY2U6IFN0YXRDYW4gQ2Vuc3VzIDIwMTYgdmlhIGNhbmNlbnN1cyAmIENlbnN1c01hcHBlci5jYSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiMSBkb3QgPSAiLHNjYWxlLCIgcGVvcGxlIikpICsgCiAgZG90X2RlbnNpdHkuZG90c19tYXAoZG90cz1kb3RzLmRiLGFscGhhPTAuNzUsc2l6ZT0wLjI1KQoKIyBzYXZlIGltYWdlIGZvciBsYXRlcgpnZ3NhdmUoJy4uL2ltYWdlcy9yZWNlbnRfaW1taWdyYW50cy5wbmcnLHdpZHRoPTI2LGhlaWdodD0yNikKYGBgCgo=